package com.qijian.flowable.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.qijian.common.annotation.PlugAnn;
import com.qijian.common.annotation.PlugParam;
import com.qijian.common.core.domain.entity.FlowableDel;
import com.qijian.common.enums.ApprovalDescriptionEnum;
import com.qijian.common.utils.SecurityUtils;
import com.qijian.common.utils.StringUtils;
import com.qijian.common.utils.mybatis.WhereEntityTool;
import com.qijian.flowable.common.cmd.AddCcIdentityLinkCmd;
import com.qijian.flowable.common.enums.CommentTypeEnum;
import com.qijian.flowable.constant.FlowableConstant;
import com.qijian.flowable.domain.FlowableRecord;
import com.qijian.flowable.domain.query.FlowableRecordQuery;
import com.qijian.flowable.domain.query.ProcessInstanceQueryVo;
import com.qijian.flowable.domain.vo.ProcessDefinitionVo;
import com.qijian.flowable.domain.vo.ProcessInstanceVo;
import com.qijian.flowable.domain.vo.TaskVo;
import com.qijian.flowable.mapper.FlowableCommonMapper;
import com.qijian.flowable.service.IFlowableRecordPowerService;
import com.qijian.flowable.service.IFlowableRecordService;
import com.qijian.flowable.service.IFlowableTaskService;
import com.qijian.flowable.service.IProcessInstanceService;
import com.qijian.flowable.util.ExpressionCmd;
import lombok.RequiredArgsConstructor;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 庄金明
 * @date 2020年3月23日
 */
@Service
@RequiredArgsConstructor
public class ProcessInstanceServiceImpl implements IProcessInstanceService {
    private final ManagementService managementService;
    private final RuntimeService runtimeService;
    private final HistoryService historyService;
    private final PermissionServiceImpl permissionService;
    private final IFlowableTaskService flowableTaskService;
    private final TaskService taskService;
    private final RepositoryService repositoryService;
    private final IFlowableRecordService flowableRecordService;
    private final IFlowableRecordPowerService flowableRecordPowerService;
    private final FlowableCommonMapper flowableCommonMapper;

    private final ProcessEngine processEngine;

    @Override
    public ProcessInstance getProcessInstanceById(String processInstanceId) {
        ProcessInstance processInstance =
                runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            throw new FlowableObjectNotFoundException("No process instance found with id " + processInstanceId);
        }
        return processInstance;
    }

    @Override
    public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance =
                historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            throw new FlowableObjectNotFoundException("No process instance found with id " + processInstanceId);
        }
        return historicProcessInstance;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> start(ProcessInstanceVo processInstanceRequest) {
        Map<String, Object> result = new HashMap<>(4);
        String processDefinitionId = StringUtils.trimToEmpty(processInstanceRequest.getProcessDefinitionId());
        String processDefinitionKey = StringUtils.trimToEmpty(processInstanceRequest.getProcessDefinitionKey());
        if (processDefinitionId.length() == 0 && processDefinitionKey.length() == 0) {
            throw new FlowableException("request param both processDefinitionId and processDefinitionKey is not found");
        } else if (processDefinitionId.length() != 0 && processDefinitionKey.length() != 0) {
            throw new FlowableException("request param both processDefinitionId and processDefinitionKey is found");
        }

        String userId;
        String nickName;
        if (ObjectUtil.isNull(processInstanceRequest.getValues().get("createId"))) {
            userId = SecurityUtils.getUserId().toString();
            nickName = SecurityUtils.getNickName();
        } else {
            userId = processInstanceRequest.getValues().get("createId").toString();
            nickName = processInstanceRequest.getValues().get("nickName").toString();
        }

        if (ObjectUtil.isNull(processInstanceRequest.getValues().get("deptId"))) {
            Long deptId = SecurityUtils.getDeptId();
            processInstanceRequest.getValues().put("deptId", deptId);
        }
        int jobLevel = 0;

        org.flowable.engine.repository.ProcessDefinition definition = permissionService.validateReadPermissionOnProcessDefinition(
                processDefinitionId, processDefinitionKey, processInstanceRequest.getTenantId(), userId);
        Map<String, Object> startVariables;
        if (processInstanceRequest.getValues() != null && !processInstanceRequest.getValues().isEmpty()) {
            startVariables = processInstanceRequest.getValues();
            // 默认设置流程启动人变量 __initiator__
        } else {
            startVariables = new HashMap<>(1);
            // 默认设置流程启动人变量 __initiator__
        }

        // 上级消息推送
        Long parent = null;

        // 查找下一节点是否UEL表达式用户
        org.flowable.engine.repository.ProcessDefinition pde = repositoryService.getProcessDefinition(processInstanceRequest.getProcessDefinitionId());
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
        for (Process process : bpmnModel.getProcesses()) {
            Map<String, FlowElement> map = process.getFlowElementMap();
            FlowElement value = process.getFlowElementMap().get(FlowableConstant.INITIATOR);
            if (value instanceof UserTask) {
                List<SequenceFlow> userTasks = ((UserTask) value).getOutgoingFlows();
                if (CollectionUtil.isNotEmpty(userTasks)) {
                    FlowElement target = userTasks.get(0).getTargetFlowElement();
                    if (target instanceof UserTask) {
                        UserTask userTask = (UserTask) target;
                        if (StringUtils.isNotBlank(userTask.getAssignee()) && StringUtils.checkUEL(userTask.getAssignee())) {
                            // 填充uel表达式
                            String[] args = userTask.getId().split("___");
                            if (args.length > 1) {
                                if ("select_user".equals(args[1])) {
                                    String userName = (String) startVariables.get(args[2]);
                                    if (StringUtils.isBlank(userName)) {
                                        throw new FlowableException("用户信息错误！");
                                    }
                                    Long execUserId = flowableCommonMapper.selectuserName(userName);
                                    startVariables.put(args[2], execUserId.toString());
                                    parent = execUserId;
                                } else if ("select_dept".equals(args[1])) {
                                    String deptName = (String) startVariables.get(args[2]);
                                    if (StringUtils.isBlank(deptName)) {
                                        throw new FlowableException("部门信息错误！");
                                    }
                                    Long leaderUserId = flowableCommonMapper.selectUserDeptLeader(deptName);
                                    if (leaderUserId == null) {
                                        throw new FlowableException("部门负责人未设置！");
                                    }
                                    startVariables.put(args[2], leaderUserId.toString());
                                    parent = leaderUserId;
                                } else {
                                    parent = FlowableTaskServiceImpl.getLeader(startVariables, args, parent, startVariables, flowableCommonMapper);
                                }
                                if (parent != null) {
                                    result.put("parent", parent);
                                }
                            }
                            break;
                        } else {
                            if (CollectionUtil.isNotEmpty(userTask.getCandidateUsers())) {
                                result.put("users", userTask.getCandidateUsers());
                            }
                            if (CollectionUtil.isNotEmpty(userTask.getCandidateGroups())) {
                                result.put("groups", userTask.getCandidateGroups());
                            }
                        }
                    }
                    else if (target instanceof InclusiveGateway) {
                        List<SequenceFlow> inclusiveGatewayOutgoingFlows = ((InclusiveGateway) target).getOutgoingFlows();
                        List<SequenceFlow> sequenceFlows = this.getSequenceFlow(startVariables, inclusiveGatewayOutgoingFlows);
                        for (SequenceFlow sequenceFlow: sequenceFlows) {
                            target = this.getFlowElement(map.values(), sequenceFlow.getTargetRef());
                            if (target instanceof UserTask) {
                                UserTask userTask = (UserTask) target;
                                if (StringUtils.isNotBlank(userTask.getAssignee()) && StringUtils.checkUEL(userTask.getAssignee())) {
                                    // 填充uel表达式
                                    String[] args = userTask.getId().split("___");
                                    if (args.length > 1) {
                                        if ("select_user".equals(args[1])) {
                                            String userName = (String) startVariables.get(args[2]);
                                            if (StringUtils.isBlank(userName)) {
                                                throw new FlowableException("用户信息错误！");
                                            }
                                            Long execUserId = flowableCommonMapper.selectuserName(userName);
                                            startVariables.put(args[2], execUserId.toString());
                                            parent = execUserId;
                                        } else if ("select_dept".equals(args[1])) {
                                            String deptName = (String) startVariables.get(args[2]);
                                            if (StringUtils.isBlank(deptName)) {
                                                throw new FlowableException("部门信息错误！");
                                            }
                                            Long leaderUserId = flowableCommonMapper.selectUserDeptLeader(deptName);
                                            if (leaderUserId == null) {
                                                throw new FlowableException("部门负责人未设置！");
                                            }
                                            startVariables.put(args[2], leaderUserId.toString());
                                            parent = leaderUserId;
                                        } else {
                                            parent = FlowableTaskServiceImpl.getLeader(startVariables, args, parent, startVariables, flowableCommonMapper);
                                        }
                                        if (parent != null) {
                                            result.put("parent", parent);
                                        }
                                    }
                                } else {
                                    if (CollectionUtil.isNotEmpty(userTask.getCandidateUsers())) {
                                        result.put("users", userTask.getCandidateUsers());
                                    }
                                    if (CollectionUtil.isNotEmpty(userTask.getCandidateGroups())) {
                                        result.put("groups", userTask.getCandidateGroups());
                                    }
                                }
                            }
                        }

                    }
                }
            }
        }
        startVariables.put("jobLevel", 0);
        startVariables.put(FlowableConstant.INITIATOR, userId);


        Authentication.setAuthenticatedUserId(userId);

        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
        processInstanceBuilder.processDefinitionId(definition.getId());
        String title = nickName.indexOf(definition.getName())> -1 ? nickName: nickName + definition.getName();
        // 流程实例标题
        if (processInstanceRequest.getValues().get("mainFlowType") != null && processInstanceRequest.getValues().get("mainFlowType").toString().startsWith("flow_re_knife")) {
            String shortNameEq =   processInstanceRequest.getValues().get("eqpName") == null ? "" : processInstanceRequest.getValues().get("eqpName") + "";
            title = title + "_" + (processInstanceRequest.getValues().get("shortName") == null ? shortNameEq : processInstanceRequest.getValues().get("shortName") + "");
        }
        processInstanceBuilder.name(title);
        // 业务key
        processInstanceBuilder.businessKey(processInstanceRequest.getBusinessKey());
        processInstanceBuilder.variables(startVariables);

        ProcessInstance instance = processInstanceBuilder.start();
        String processInstanceId = instance.getProcessInstanceId();
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        // 添加记录
        FlowableRecord recordAdd = new FlowableRecord();
        recordAdd.setBusinessValues(CollectionUtil.isNotEmpty(processInstanceRequest.getValues()) ? (String) processInstanceRequest.getValues().get("processInstanceFormData") : null);
        recordAdd.setBusinessKey(tasks.get(0).getFormKey().split("\\.")[0]);
        recordAdd.setProcessDefintionId(instance.getProcessDefinitionId());
        recordAdd.setProcessInstanceId(instance.getProcessInstanceId());
        recordAdd.setRecordName(title);
        recordAdd.setRecordType(instance.getProcessDefinitionKey());
        if (processDefinitionKey.startsWith("flow_re")) {
            recordAdd.setRecordType(instance.getProcessVariables().get("mainFlowType").toString());
        }
        recordAdd.setCategory(recordAdd.getRecordType().split("_")[0]);
        recordAdd.setUserId(Long.parseLong(userId));
        recordAdd.setStatus(ApprovalDescriptionEnum.B.getValue());
        recordAdd.setCreateTime(new Timestamp(System.currentTimeMillis()));
        recordAdd.setSourceId(processInstanceRequest.getSourceId());

        if (instance.getProcessDefinitionKey().startsWith("flow_process_performance")) {
            recordAdd.setProjectNos(flowableRecordService.getProjectNoList(processInstanceRequest.getSourceId(),"flow_process_performance"));
        }
        if (instance.getProcessDefinitionKey().startsWith("flow_process_craft_dispatch")) {
            recordAdd.setProjectNos(flowableRecordService.getProjectNoList(processInstanceRequest.getSourceId(),"flow_process_craft_dispatch"));
        }
        if (instance.getProcessDefinitionKey().startsWith("flow_fin_cost_ex")) {
            recordAdd.setProjectNos(flowableRecordService.getProjectNoList(processInstanceRequest.getSourceId(),"flow_fin_cost_ex"));
        }

        flowableRecordService.save(recordAdd);

        for (Task task : tasks) {
            // 约定：发起者节点为 __initiator__ ,则自动完成任务
            if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) {
                flowableTaskService.addComment(task.getId(), processInstanceId, userId, CommentTypeEnum.TJ, null);
                if (ObjectUtil.isEmpty(task.getAssignee())) {
                    taskService.setAssignee(task.getId(), userId);
                }
                taskService.complete(task.getId());
                if (ObjectUtil.isNotEmpty(processInstanceRequest.getCcToVos())) {
                    managementService.executeCommand(new AddCcIdentityLinkCmd(processInstanceId, task.getId(), userId
                            , processInstanceRequest.getCcToVos()));

                }
            }
        }

        // 添加流程记录查看权限
        FlowableTaskServiceImpl.addRecordPower(processInstanceRequest.getCcToVos() != null ? Arrays.asList(processInstanceRequest.getCcToVos()) : null, instance.getProcessInstanceId(), flowableRecordPowerService, userId);

        // 下一审批人是否自己
        tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if (CollectionUtil.isNotEmpty(tasks)) {
            List<HistoricIdentityLink> taskIdentityLinks = historyService.getHistoricIdentityLinksForTask(tasks.get(0).getId());
            if (taskIdentityLinks.size() == 1 && taskIdentityLinks.get(0).getUserId().equals(userId)) {
                TaskVo vo = new TaskVo();
                vo.setTaskId(tasks.get(0).getId());
                vo.setValues(processInstanceRequest.getValues());
                vo.setCcToVos(processInstanceRequest.getCcToVos());
                vo.setMessage("自动审核");
                vo.setUserId(userId);
                // 当前提交人与后置审批人一致
                vo.setTaskId(tasks.get(0).getId());

                // 认领
                flowableTaskService.addComment(vo.getTaskId(), processInstanceId, userId, CommentTypeEnum.RL, "自动认领");
                taskService.claim(vo.getTaskId(), userId);

                flowableTaskService.completeTaskSystem(vo, jobLevel);
            }
        }
        result.put("title", instance.getName());
        result.put("processInstanceId", instance.getProcessInstanceId());
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startAdmin(ProcessInstanceVo processInstanceRequest) {
        String processDefinitionId = StringUtils.trimToEmpty(processInstanceRequest.getProcessDefinitionId());
        String processDefinitionKey = StringUtils.trimToEmpty(processInstanceRequest.getProcessDefinitionKey());
        if (processDefinitionId.length() == 0 && processDefinitionKey.length() == 0) {
            throw new FlowableException("request param both processDefinitionId and processDefinitionKey is not found");
        } else if (processDefinitionId.length() != 0 && processDefinitionKey.length() != 0) {
            throw new FlowableException("request param both processDefinitionId and processDefinitionKey is found");
        }
        String userId = "1";

        Map<String, Object> startVariables;
        if (processInstanceRequest.getValues() != null && !processInstanceRequest.getValues().isEmpty()) {
            startVariables = processInstanceRequest.getValues();
            // 默认设置流程启动人变量 __initiator__
        } else {
            startVariables = new HashMap<>(1);
            // 默认设置流程启动人变量 __initiator__
        }

        startVariables.put(FlowableConstant.INITIATOR, userId);

        ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
        processInstanceBuilder.processDefinitionId(processInstanceRequest.getProcessDefinitionId());
        // 流程实例标题
        processInstanceBuilder.name(startVariables.get("nickName").toString() + processInstanceRequest.getProcessDefinitionName());
        // 业务key
        processInstanceBuilder.businessKey(processInstanceRequest.getBusinessKey());
        processInstanceBuilder.variables(startVariables);

        ProcessInstance instance = processInstanceBuilder.start();
        String processInstanceId = instance.getProcessInstanceId();
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

        // 添加记录
        FlowableRecord recordAdd = new FlowableRecord();
        recordAdd.setBusinessValues(CollectionUtil.isNotEmpty(processInstanceRequest.getValues()) ? (String) processInstanceRequest.getValues().get("processInstanceFormData") : null);
        recordAdd.setBusinessKey(tasks.get(0).getFormKey().split("\\.")[0]);
        recordAdd.setProcessDefintionId(instance.getProcessDefinitionId());
        recordAdd.setProcessInstanceId(instance.getProcessInstanceId());
        recordAdd.setRecordName(instance.getName());
        recordAdd.setRecordType(instance.getProcessDefinitionKey());
        recordAdd.setCategory(recordAdd.getRecordType().split("_")[0]);
        recordAdd.setUserId(Long.parseLong(userId));
        recordAdd.setStatus(ApprovalDescriptionEnum.B.getValue());
        recordAdd.setCreateTime(new Timestamp(System.currentTimeMillis()));
        flowableRecordService.save(recordAdd);

        for (Task task : tasks) {
            // 约定：发起者节点为 __initiator__ ,则自动完成任务
            if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) {
                flowableTaskService.addComment(task.getId(), processInstanceId, userId, CommentTypeEnum.TJ, "系统创建");
                if (ObjectUtil.isEmpty(task.getAssignee())) {
                    taskService.setAssignee(task.getId(), userId);
                }
                taskService.complete(task.getId());
                if (ObjectUtil.isNotEmpty(processInstanceRequest.getCcToVos())) {
                    managementService.executeCommand(new AddCcIdentityLinkCmd(processInstanceId, task.getId(), userId
                            , processInstanceRequest.getCcToVos()));

                }
            }
        }

        // 添加流程记录查看权限
        FlowableTaskServiceImpl.addRecordPower(processInstanceRequest.getCcToVos() != null ? Arrays.asList(processInstanceRequest.getCcToVos()) : null, instance.getProcessInstanceId(), flowableRecordPowerService, userId);
        return instance.getProcessInstanceId();
    }

    @Override
    @PlugAnn(groupClass = FlowableDel.Approval.class)
    @Transactional(rollbackFor = Exception.class)
    public void delete(@PlugParam String processInstanceId, boolean cascade, String deleteReason) {
        HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(processInstanceId);
        if (historicProcessInstance.getEndTime() != null) {
            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            return;
        }
        ExecutionEntity executionEntity = (ExecutionEntity) getProcessInstanceById(processInstanceId);
        if (StringUtils.isNotBlank(executionEntity.getSuperExecutionId())) {
            throw new FlowableException("This is a subprocess");
        }
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
        if (cascade) {
            historyService.deleteHistoricProcessInstance(processInstanceId);
        }
        // 删除记录
        FlowableRecordQuery query = new FlowableRecordQuery();
        query.setProcessInstanceId(processInstanceId);
        flowableRecordService.remove(WhereEntityTool.invoke(query));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activate(String processInstanceId) {
        ProcessInstance processInstance = getProcessInstanceById(processInstanceId);
        if (!processInstance.isSuspended()) {
            throw new FlowableException("Process instance is not suspended with id " + processInstanceId);
        }
        runtimeService.activateProcessInstanceById(processInstance.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspend(String processInstanceId) {
        ProcessInstance processInstance = getProcessInstanceById(processInstanceId);
        if (processInstance.isSuspended()) {
            throw new FlowableException("Process instance is already suspended with id {0}" + processInstanceId);
        }
        runtimeService.suspendProcessInstanceById(processInstance.getId());
    }

    @Override
    public List<ProcessDefinitionVo> listMyInvolvedSummary(ProcessInstanceQueryVo processInstanceQueryVo) {
        return flowableCommonMapper.listMyInvolvedSummary(processInstanceQueryVo);
//        List<CategoryVo> result = new ArrayList<>();
//        Map<String, List<ProcessDefinition>> categorysByParent = new HashMap<>();
//        for (ProcessDefinition vo : vos) {
//            List<ProcessDefinition> childs = categorysByParent.computeIfAbsent(vo.getCategory(),
//                    k -> new ArrayList<>());
//            childs.add(vo);
//        }
//        for (Map.Entry<String, List<ProcessDefinition>> entry : categorysByParent.entrySet()) {
//            ProcessDefinition processDefinition = entry.getValue().iterator().next();
//            CategoryVo aCategoryVo = new CategoryVo();
//            aCategoryVo.setCategory(entry.getKey());
//            aCategoryVo.setProcessDefinitionVoList(entry.getValue());
//            String categoryName = processDefinition.getCategoryName();
//            aCategoryVo.setCategoryName(categoryName);
//            String instanceName = processDefinition.getProcessInstanceName();
//            aCategoryVo.setProcessInstanceName(instanceName);
//            String type = processDefinition.getCategoryType();
//            aCategoryVo.setCategoryType(type);
//            aCategoryVo.setId(processDefinition.getId());
//            aCategoryVo.setTaskId(processDefinition.getTaskId());
//            aCategoryVo.setStartTime(processDefinition.getStartTime());
//            result.add(aCategoryVo);
//        }
//        return result;
    }


    private List<SequenceFlow> getSequenceFlow(Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows) {
        List<SequenceFlow> sequenceFlows = this.getSequenceFlows(variableMap, outgoingFlows);
        if (null != sequenceFlows && sequenceFlows.size() > 0) {
            return sequenceFlows;
        }

        return outgoingFlows;
    }

    /**
     * 根据传入的变量，计算出表达式成立的SequenceFlow连线集合
     *
     * @param variableMap   变量map
     * @param outgoingFlows 输出流
     * @return SequenceFlow连线集合
     */
    private List<SequenceFlow> getSequenceFlows(Map<String, Object> variableMap, List<SequenceFlow> outgoingFlows) {
        List<SequenceFlow> sequenceFlows;
        sequenceFlows = outgoingFlows.stream().filter(item -> {
            try {
                return this.getElValue(null,item.getConditionExpression(), variableMap);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }).collect(Collectors.toList());
        return sequenceFlows;
    }

    public boolean getElValue(String processInstanceId, String exp, Map<String, Object> variableMap) {
        if (StringUtils.isBlank(exp)) {
            return true;
        }
        boolean flag = managementService.executeCommand(new ExpressionCmd(processEngine, processInstanceId, exp, variableMap));
        return flag;
    }

    private FlowElement getFlowElement(Collection<FlowElement> flowElements, String targetRef) {
        return flowElements.stream().filter(flowElement -> targetRef.equals(flowElement.getId())).findFirst().orElse(null);
    }
}
